﻿using System.ComponentModel;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Collections.Generic;
using System.Net;
using System;
using System.Configuration;
using System.Text;
namespace GBlog.Core.Recaptcha
{
    public class RecaptchaControl : WebControl, IValidator
    {
        private const string RECAPTCHA_CHALLENGE_FIELD = "recaptcha_challenge_field";
        private const string RECAPTCHA_RESPONSE_FIELD = "recaptcha_response_field";
        private const string RECAPTCHA_SECURE_HOST = "https://www.google.com/recaptcha/api";
        private const string RECAPTCHA_HOST = "http://www.google.com/recaptcha/api";
        private RecaptchaResponse recaptchaResponse;
        private string publicKey;
        private string privateKey;
        private string theme;
        private string language;
        private Dictionary<string, string> customTranslations;
        private string customThemeWidget;
        private bool skipRecaptcha;
        private bool allowMultipleInstances;
        private bool overrideSecureMode;
        private IWebProxy proxy;
        [Category("Settings"), Description("The public key from https://www.google.com/recaptcha/admin/create. Can also be set using RecaptchaPublicKey in AppSettings.")]
        public string PublicKey
        {
            get
            {
                return this.publicKey;
            }
            set
            {
                this.publicKey = value;
            }
        }
        [Category("Settings"), Description("The private key from https://www.google.com/recaptcha/admin/create. Can also be set using RecaptchaPrivateKey in AppSettings.")]
        public string PrivateKey
        {
            get
            {
                return this.privateKey;
            }
            set
            {
                this.privateKey = value;
            }
        }
        [Category("Appearance"), DefaultValue("red"), Description("The theme for the reCAPTCHA control. Currently supported values are 'red', 'white', 'blackglass', 'clean', and 'custom'.")]
        public string Theme
        {
            get
            {
                return this.theme;
            }
            set
            {
                this.theme = value;
            }
        }
        [Category("Appearance"), DefaultValue(null), Description("UI language for the reCAPTCHA control. Currently supported values are 'en', 'nl', 'fr', 'de', 'pt', 'ru', 'es', and 'tr'.")]
        public string Language
        {
            get
            {
                return this.language;
            }
            set
            {
                this.language = value;
            }
        }
        [Category("Appearance"), DefaultValue(null)]
        public Dictionary<string, string> CustomTranslations
        {
            get
            {
                return this.customTranslations;
            }
            set
            {
                this.customTranslations = value;
            }
        }
        [Category("Appearance"), DefaultValue(null), Description("When using custom theming, this is a div element which contains the widget. ")]
        public string CustomThemeWidget
        {
            get
            {
                return this.customThemeWidget;
            }
            set
            {
                this.customThemeWidget = value;
            }
        }
        [Category("Settings"), DefaultValue(false), Description("Set this to true to stop reCAPTCHA validation. Useful for testing platform. Can also be set using RecaptchaSkipValidation in AppSettings.")]
        public bool SkipRecaptcha
        {
            get
            {
                return this.skipRecaptcha;
            }
            set
            {
                this.skipRecaptcha = value;
            }
        }
        [Category("Settings"), DefaultValue(false), Description("Set this to true to enable multiple reCAPTCHA on a single page. There may be complication between controls when this is enabled.")]
        public bool AllowMultipleInstances
        {
            get
            {
                return this.allowMultipleInstances;
            }
            set
            {
                this.allowMultipleInstances = value;
            }
        }
        [Category("Settings"), DefaultValue(false), Description("Set this to true to override reCAPTCHA usage of Secure API.")]
        public bool OverrideSecureMode
        {
            get
            {
                return this.overrideSecureMode;
            }
            set
            {
                this.overrideSecureMode = value;
            }
        }
        [Category("Settings"), Description("Set this to override proxy used to validate reCAPTCHA.")]
        public IWebProxy Proxy
        {
            get
            {
                return this.proxy;
            }
            set
            {
                this.proxy = value;
            }
        }
        [DefaultValue("The verification words are incorrect."), Localizable(true)]
        public string ErrorMessage
        {
            get
            {
                return (this.recaptchaResponse != null) ? this.recaptchaResponse.ErrorMessage : null;
            }
            set
            {
                throw new NotImplementedException("This setter is not implemented.");
            }
        }
        [Browsable(false)]
        public bool IsValid
        {
            get
            {
                return this.recaptchaResponse == null || this.recaptchaResponse.IsValid;
            }
            set
            {
                throw new NotImplementedException("This setter is not implemented.");
            }
        }
        public RecaptchaControl()
        {
            this.publicKey = ConfigurationManager.AppSettings["Recaptcha_Public_Key"];
            this.privateKey = ConfigurationManager.AppSettings["Recaptcha_Private_Key"];
            if (!bool.TryParse(ConfigurationManager.AppSettings["Recaptcha_Skip_Validation"], out this.skipRecaptcha))
            {
                this.skipRecaptcha = false;
            }
        }
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            if (string.IsNullOrEmpty(this.PublicKey) || string.IsNullOrEmpty(this.PrivateKey))
            {
                throw new ApplicationException("reCAPTCHA needs to be configured with a public & private key.");
            }
            if (this.allowMultipleInstances || !this.CheckIfRecaptchaExists())
            {
                this.Page.Validators.Add(this);
            }
        }
        private bool CheckIfRecaptchaExists()
        {
            bool result;
            foreach (object current in this.Page.Validators)
            {
                if (current is RecaptchaControl)
                {
                    result = true;
                    return result;
                }
            }
            result = false;
            return result;
        }
        protected override void Render(HtmlTextWriter writer)
        {
            if (this.skipRecaptcha)
            {
                writer.WriteLine("reCAPTCHA validation is skipped. Set SkipRecaptcha property to false to enable validation.");
            }
            else
            {
                this.RenderContents(writer);
            }
        }
        protected override void RenderContents(HtmlTextWriter output)
        {
            output.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            output.RenderBeginTag(HtmlTextWriterTag.Script);
            output.Indent++;
            output.WriteLine("var RecaptchaOptions = {");
            output.Indent++;
            output.WriteLine("theme : '{0}',", this.theme ?? string.Empty);
            if (!string.IsNullOrEmpty(this.language))
            {
                output.WriteLine("lang : '{0}',", this.language);
            }
            if (this.customTranslations != null && this.customTranslations.Count > 0)
            {
                output.WriteLine("custom_translations : {");
                foreach (KeyValuePair<string, string> current in this.customTranslations)
                {
                    output.WriteLine("{0} : '{1}',", current.Key, current.Value);
                }
                output.WriteLine("},");
            }
            if (!string.IsNullOrEmpty(this.customThemeWidget))
            {
                output.WriteLine("custom_theme_widget : '{0}',", this.customThemeWidget);
            }
            output.WriteLine("tabindex : {0}", base.TabIndex);
            output.Indent--;
            output.WriteLine("};");
            output.Indent--;
            output.RenderEndTag();
            output.AddAttribute(HtmlTextWriterAttribute.Type, "text/javascript");
            output.AddAttribute(HtmlTextWriterAttribute.Src, this.GenerateChallengeUrl(false), false);
            output.RenderBeginTag(HtmlTextWriterTag.Script);
            output.RenderEndTag();
            output.RenderBeginTag(HtmlTextWriterTag.Noscript);
            output.Indent++;
            output.AddAttribute(HtmlTextWriterAttribute.Src, this.GenerateChallengeUrl(true), false);
            output.AddAttribute(HtmlTextWriterAttribute.Width, "500");
            output.AddAttribute(HtmlTextWriterAttribute.Height, "300");
            output.AddAttribute("frameborder", "0");
            output.RenderBeginTag(HtmlTextWriterTag.Iframe);
            output.RenderEndTag();
            output.WriteBreak();
            output.AddAttribute(HtmlTextWriterAttribute.Name, "recaptcha_challenge_field");
            output.AddAttribute(HtmlTextWriterAttribute.Rows, "3");
            output.AddAttribute(HtmlTextWriterAttribute.Cols, "40");
            output.RenderBeginTag(HtmlTextWriterTag.Textarea);
            output.RenderEndTag();
            output.AddAttribute(HtmlTextWriterAttribute.Name, "recaptcha_response_field");
            output.AddAttribute(HtmlTextWriterAttribute.Value, "manual_challenge");
            output.AddAttribute(HtmlTextWriterAttribute.Type, "hidden");
            output.RenderBeginTag(HtmlTextWriterTag.Input);
            output.RenderEndTag();
            output.Indent--;
            output.RenderEndTag();
        }
        public void Validate()
        {
            if (this.Page.IsPostBack && this.Visible && this.Enabled && !this.skipRecaptcha)
            {
                if (this.recaptchaResponse == null)
                {
                    if (this.Visible && this.Enabled)
                    {
                        RecaptchaValidator recaptchaValidator = new RecaptchaValidator();
                        recaptchaValidator.PrivateKey = this.PrivateKey;
                        recaptchaValidator.RemoteIP = this.Page.Request.UserHostAddress;
                        recaptchaValidator.Challenge = this.Context.Request.Form["recaptcha_challenge_field"];
                        recaptchaValidator.Response = this.Context.Request.Form["recaptcha_response_field"];
                        recaptchaValidator.Proxy = this.proxy;
                        if (recaptchaValidator.Challenge == null)
                        {
                            this.recaptchaResponse = RecaptchaResponse.InvalidChallenge;
                        }
                        else
                        {
                            if (recaptchaValidator.Response == null)
                            {
                                this.recaptchaResponse = RecaptchaResponse.InvalidResponse;
                            }
                            else
                            {
                                this.recaptchaResponse = recaptchaValidator.Validate();
                            }
                        }
                    }
                }
            }
            else
            {
                this.recaptchaResponse = RecaptchaResponse.Valid;
            }
        }
        private string GenerateChallengeUrl(bool noScript)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append((this.Context.Request.IsSecureConnection || this.overrideSecureMode) ? "https://www.google.com/recaptcha/api" : "http://www.google.com/recaptcha/api");
            stringBuilder.Append(noScript ? "/noscript?" : "/challenge?");
            stringBuilder.AppendFormat("k={0}", this.PublicKey);
            return stringBuilder.ToString();
        }
    }
}